﻿using Autofac;
using Autofac.Builder;
using Autofac.Core;
using Autofac.Features.ResolveAnything;
using AutofacContrib.Moq;
using Moq;

namespace MoqContrib.AutoMock.Autofac
{
    public class AutoMockProvider : IAutoMockProvider
    {
        public AutoMockProvider(MockRepository mockRepository)
        {
           var builder = new ContainerBuilder();
           builder.RegisterInstance(mockRepository);
           builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
           builder.RegisterSource(new MoqRegistrationHandler());
           Container = builder.Build(); 
        }
    
    
        /// <summary>
        /// <see cref="IContainer"/> that handles the component resolution.
        /// </summary>
        public IContainer Container { get; private set; }
        
        /// <summary>
        /// Finds (creating if needed) the actual mock for the provided type
        /// </summary>
        /// <typeparam name="T">Type to mock</typeparam>
        /// <param name="parameters">Optional parameters</param>
        /// <returns>Mock</returns>
        public Mock<T> Mock<T>(params object[] parameters) where T : class
        {
            var obj = (IMocked<T>)Create<T>();
            return obj.Mock;
        }
    
        /// <summary>
        /// Resolve the specified type in the container (register it if needed)
        /// </summary>
        /// <typeparam name="T">Service</typeparam>
        /// <param name="parameters">Optional parameters</param>
        /// <returns>The service.</returns>
        public T Create<T>(params Parameter[] parameters)
        {
            return Container.Resolve<T>(parameters);
        }

    /// <summary>
    /// Resolve the specified type in the container (register it if needed)
    /// </summary>
    /// <typeparam name="TService">Service</typeparam>
    /// <typeparam name="TImplementation">The implementation of the service.</typeparam>
    /// <param name="parameters">Optional parameters</param>
    /// <returns>The service.</returns>
    public TService Provide<TService, TImplementation>(params Parameter[] parameters)
    {
            Container.ComponentRegistry.Register(
                RegistrationBuilder.ForType<TImplementation>().As<TService>().InstancePerLifetimeScope().CreateRegistration());

            return Container.Resolve<TService>(parameters);
        }

        /// <summary>
        /// Resolve the specified type in the container (register specified instance if needed)
        /// </summary>
        /// <typeparam name="TService">Service</typeparam>
        /// <returns>The instance resolved from container.</returns>
        public TService Provide<TService>(TService instance)
            where TService : class
        {
            Container.ComponentRegistry.Register(
                RegistrationBuilder.ForDelegate((c, p) => instance).InstancePerLifetimeScope().CreateRegistration());

            return Container.Resolve<TService>();
        }
    }
}